using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace DotNetCommon.Accessors;
/// <summary>
/// 通过 反射+编译表达式 快速读写类的属性, 示例:
/// <code>
/// //准备类
/// public class Person
/// {
///     public int Id { get; set; }
///     public string Name { get; set; }
/// }
/// 
/// //构建Accessor(忽略大小写)
/// var accessor = Accessor.Build&lt;Person&gt;(true);
/// //准备Person类实例
/// var person = new Person() { Id = 1, Name = "普通用户"};
/// //读取属性 Id
/// Console.WriteLine(accessor[person, "Id"]);//output: 1
/// //写入属性 Name
/// accessor[person, "name"] = "小明";
/// Console.WriteLine(person.Name);//output: "小明"
/// </code>
/// </summary>
/// <remarks>
/// 可以选择的构建方法:
/// <list type="bullet">
/// <item>通过泛型: var accessor = Accessor.Build&lt;Person&gt;(true);</item>
/// <item>通过对象实例: var accessor = Accessor.Build(new Person(), true);</item>
/// <item>通过Type: var accessor = Accessor.Build(typeof(Person), true);</item>
/// </list>
/// </remarks>
public abstract class Accessor
{
    private static readonly ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor> _objectAccessorCache = new();
    private static readonly ConcurrentDictionary<(Type type, bool ignoreCase, bool includeNonPublic), ObjectAccessor> _genericAccessorCache = new();
    protected Accessor(IReflect type, bool ignoreCase, bool includeNonPublic)
    {
        Type = type;
        IgnoreCase = ignoreCase;
        IncludesNonPublic = includeNonPublic;

        Comparer = IgnoreCase ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;

        var flags = BindingFlags.Public | BindingFlags.Instance;
        if (IncludesNonPublic) flags |= BindingFlags.NonPublic;

        //属性
        var props = Type.GetProperties(flags);
        Properties = new SortedList<string, PropertyInfo>(props.Length, Comparer);
        foreach (var prop in props) Properties[prop.Name] = prop;

        //字段
        var fileds = Type.GetFields(flags);
        Fields = new SortedList<string, FieldInfo>(fileds.Length, Comparer);
        foreach (var field in fileds)
        {
            //忽略属性的字段
            if (field.Name.EndsWith("__BackingField")) continue;
            Fields[field.Name] = field;
        }
    }

    protected StringComparer Comparer { get; }

    /// <summary>
    /// 当前反射的对象类型
    /// </summary>
    public IReflect Type { get; }

    /// <summary>
    /// 获取的属性是否是忽略大小的
    /// </summary>
    public bool IgnoreCase { get; }

    /// <summary>
    /// 获取的属性是否包含非public类型的
    /// </summary>
    public bool IncludesNonPublic { get; }

    /// <summary>
    /// 所有能获取到的属性信息
    /// </summary>
    public SortedList<string, PropertyInfo> Properties { get; }

    /// <summary>
    /// 所有能获取到的字段信息
    /// </summary>
    public SortedList<string, FieldInfo> Fields { get; }

    /// <summary>
    /// 根据给定的对象创建<see cref="ObjectAccessor"/>实例
    /// </summary>
    /// <param name="instance">给定的对象</param>
    /// <param name="ignoreCase">是否忽略大小写</param>
    /// <param name="includeNonPublic">是否包含非public属性</param>
    [DebuggerStepThrough]
    public static ObjectAccessor Build(object instance, bool ignoreCase = false, bool includeNonPublic = false)
    {
        AssertUtil.NotNull(instance);
        return Build(instance.GetType(), ignoreCase, includeNonPublic);
    }

    /// <summary>
    /// 根据给定的对象类型创建<see cref="ObjectAccessor"/>实例
    /// </summary>
    /// <param name="instanceType">给定的对象类型</param>
    /// <param name="ignoreCase">是否忽略大小写</param>
    /// <param name="includeNonPublic">是否包含非public属性</param>
    [DebuggerStepThrough]
    public static ObjectAccessor Build(Type instanceType, bool ignoreCase = false, bool includeNonPublic = false)
    {
        AssertUtil.NotNull(instanceType);
        return _objectAccessorCache.GetOrAdd((instanceType, ignoreCase, includeNonPublic), _ => new ObjectAccessor(instanceType, ignoreCase, includeNonPublic));
    }

    /// <summary>
    /// 根据给定的对象类型创建<see cref="GenericAccessor{TInstance}"/>实例
    /// </summary>
    /// <typeparam name="TInstance">对象类型</typeparam>
    /// <param name="ignoreCase">是否忽略大小写</param>
    /// <param name="includeNonPublic">是否包含非public属性</param>
    [DebuggerStepThrough]
    public static GenericAccessor<TInstance> Build<TInstance>(
        bool ignoreCase = false, bool includeNonPublic = false) where TInstance : class
    {
        return (GenericAccessor<TInstance>)_genericAccessorCache.GetOrAdd((typeof(TInstance), ignoreCase, includeNonPublic), _ => new GenericAccessor<TInstance>(ignoreCase, includeNonPublic));
    }
}